#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <set>
#include "Log.hpp"
#include "InetAddr.hpp"

using namespace log_ns;
class Command
{
public:
    Command()
    {
        // 白名单
        _safe_command.insert("ls");
        _safe_command.insert("touch"); // touch filename
        _safe_command.insert("pwd");
        _safe_command.insert("whoami");
        _safe_command.insert("which"); // which pwd
    }
    ~Command()
    { 
    }
    //漏洞： ls; rm -rf /
    //检查是否属于白名单里的命令
    bool SafeCheck(const std::string &cmdstr)
    {
        for(auto &cmd :_safe_command)
        {
            //进行前缀的比较
            if(strncmp(cmd.c_str(), cmdstr.c_str(), cmd.size()) == 0)
            {
                return true;
            }
        }
        return false;

    }

    // 约定跑的是字符串
    std::string Execute(const std::string &cmdstr)
    {
        if(!SafeCheck(cmdstr))
        {
            return "unsafe";
        }

        FILE *fp = popen(cmdstr.c_str(), "r");
        std::string result;
        if (fp)
        {
            char line[1024];
            // 从fp中读取到line里面
            while (fgets(line, sizeof(line), fp))
            {
                result += line;
            }
            //例如touch的返回值是空的，就认为成功了：
            return result.empty() ? "success" : result;
        }
        return "execute error";
    }
    // 处理命令
    void HandlerCommand(int sockfd, InetAddr addr)
    {
        // 将命令的执行当成一个 长服务（例如：在xshell当中可以隔许久再在命令行输入下一次命令）
        while (true)
        {
            // 缺点：inbuffer不是动态的，每次只能读取这么多，或者说，明明只发了5个字节，但是却还是要读取1023个字节
            char commandbuf[1024];
            // n是实际读取的字节数                                   // 当做字符串
            ssize_t n = ::recv(sockfd, commandbuf, sizeof(commandbuf) - 1, 0); //(留一个\n的位置)
            if (n > 0)
            {
                commandbuf[n] = 0;
                LOG(INFO, "get command from client %s, command: %s\n", addr.AddrStr().c_str(), commandbuf);
                // 处理命令：
                std::string result = Execute(commandbuf);
                // 2.拼接好信息之后发回去，证明消息接收成功
                ::send(sockfd, result.c_str(), result.size(), 0);
            }
            else if (n == 0) // 返回值为0，表示客户端结束（文件中读到尾）
            {
                LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());
                break;
            }
            else
            {
                LOG(ERROR, "recv error: %s\n", addr.AddrStr().c_str());
                break;
            }
        }
    }

private:
    // 只允许执行个别命令
    std::set<std::string> _safe_command;
};
